
#include <mpi.h>
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <string>
#include <fstream>
#include <sstream>
#include <vector>
#include <math.h>
#include <stdio.h>
#include <sys/time.h>
//#include <omp.h>

#define NMAX 100000

using namespace std;

// Vetor para calculo dos numeros pi até 10000 
int v[NMAX];


// contadores de palindromos e de primos
int n_pali = 0;
int n_prime = 0;


// deixa as letras todas maisculas
string toUpperCase(string s) {

  for (int i = 0; i < s.size(); i++)
    s[i]=toupper(s[i]);
  return s;
}

// Faz a tabela de primos pelo Crivo Erastótenes (calcula todos os primos de uma vez só)
int table_prime() {
    int i, j;
    int raiz;
 
    raiz=(int)sqrt(NMAX);
 
    for (i=2; i<NMAX; i++) v[i]=i;
    
    for (i=2; i<raiz; i++) {
        if (v[i]==i) {
            for (j=i+i; j<NMAX; j+=i) {
                v[j]=0; // removendo da lista
            }
        }
    }
}
 
// Acessa a tabela para verificar se é primo
bool is_prime(int value) {
    return (bool)v[value];
}


// Limpa a string de apontuacao
void remove_pontuacion(string& in)
{
    for(int i=0; i <in.length(); i++)
    {
        if(ispunct(in[i]))
          in[i]= ' ';
    }
    return;
}


int sum_char(string& word) {
    int i, out = 0;
    for(i=0; i<word.size(); i++) out += word[i];
    return out;
}


// verifica se um string é palindromo;
bool palindrome(string s) {
  if( equal(s.begin(), s.begin() + s.size()/2, s.rbegin()) )
      return true;
  else
      return false;
}

// Dado uma linha do arquivo (in), quebra em palavras (palavras entre espacos brancos)
void token(string& in) {
    istringstream iss(in);

    do
    {
        string sub;
        iss >> sub;
        if(sub.size() > 2) {
          sub = toUpperCase(sub);
          if(palindrome(sub)) {
              n_pali++;
              //cout << sub << endl;
              int sum = sum_char(sub);
              if(is_prime(sum)) 
                n_prime++;
          } 
        }
    } while (iss);

    iss.clear();
    
}


int main (int argc, char ** argv){
  int local_n_pali = 0;
  int local_n_prime = 0; 
  double       inittime,totaltime;
  
  int numberNodes, myNode, numLines = 0,i=1;
  int numPalindrome = 0, numPrime = 0, bufInt[2];
  vector<string *> text;
  unsigned int * num_msg;

  MPI::Status status;

  MPI::Init(argc, argv);
  inittime = MPI::Wtime();
  numberNodes = MPI::COMM_WORLD.Get_size();
  myNode = MPI::COMM_WORLD.Get_rank();

  
  
    if( myNode == 0 ){
      ifstream fin ("wikipedia.txt"); // ifstream = padrão ios:in
      if (fin.is_open())
      { 
        num_msg = (unsigned int*)calloc(sizeof(unsigned int) , numberNodes);
        //while send
        i=1;
        while ( !fin.eof() )
        { //enquanto end of file for false continua
          string *line;
          line = new string();
          getline(fin, *line, '\n'); // pega a linha do arquivo
          numLines++;
          if( i >= numberNodes)
            i=1;
          text.push_back(line);
          num_msg[i]++;
          i++;
        }
        i=1;
        while(i<numberNodes){
          MPI::COMM_WORLD.Send(&(num_msg[i]), 1,MPI::INT, i, 1);
          i++;
        }
        

        i=1;
        while(!text.empty()){
          string *line;
          line = text.back();
          text.pop_back();
          if( i >= numberNodes)
            i=1;
          MPI::COMM_WORLD.Send(line->c_str(), line->size() +1,MPI::CHAR, i, 2);
          
          //std::cout << *line << std::endl<<  i << std::endl;
          i++;
          delete line; 
        }
        //std::cout << "fim Master "<< std::endl;
        
        /*for reduce
        for(; 0 < numLines; numLines--){
          if( i >= numberNodes)i=0;
          MPI::COMM_WORLD.Recv(bufInt, 2, MPI::INT, i, 1, status);
          i++;
          numPalindrome += bufInt[0]; numPrime +=bufInt[1];
        }*/   
        free(num_msg);
      }else{
        return(-1);
      }
    }else{
      table_prime();
      
      num_msg = (unsigned int*)malloc(sizeof(unsigned int) * 1);
      *num_msg=0;
      MPI::COMM_WORLD.Recv(num_msg, 1, MPI::INT, 0, 1, status);
      //std::cout << "Process " << myNode << " of " << numberNodes << " received " << (*num_msg) << std::endl;
     
      
      
      i=0;
      while( i < (*num_msg) )
      {
        

        int number_amount, flag;

        MPI::COMM_WORLD.Probe(0, 2 ,status) ;
        
       
        // When probe returns, the status object has the size and other
        // attributes of the incoming message. Get the size of the message
        number_amount = status.Get_count(MPI::CHAR);
        
 
        // Allocate a buffer just big enough to hold the incoming numbers
        char* buf = (char*)malloc(sizeof(char) * number_amount);
        
        // Now receive the message with the allocated buffer
        MPI::COMM_WORLD.Recv(buf, number_amount, MPI::CHAR, 0, 2, status);
        string line(buf);
        remove_pontuacion(line);


              istringstream iss(line);

              do
              {
                string sub;
                iss >> sub;
                if(sub.size() > 2) {
                  sub = toUpperCase(sub);
                  if(palindrome(sub)) {
                    local_n_pali++;
                    //cout << sub << endl;
                    int sum = sum_char(sub);
                      if(is_prime(sum)) {
                        local_n_prime++;
                      }
                    } 
                  }
              } while (iss);

        iss.clear();
        line.clear();
        
        //std::cout << "Process " << myNode << std::endl;
        //std::cout << buf << std::endl;
        free(buf);

        i++;
      }
      char name[60];
      int resultlen;
      MPI::Get_processor_name(name, resultlen);
      cout << "Task "<< name << " Results: " << "number of words palindrome: " << local_n_pali << endl;
      cout << "Task "<< name << " Results: " << "number of words prime: " << local_n_prime << endl;
      free(num_msg);
    }
  MPI::COMM_WORLD.Reduce(&local_n_pali,&n_pali,1,MPI::INT,MPI::SUM,0);
  MPI::COMM_WORLD.Reduce(&local_n_prime,&n_prime,1,MPI::INT,MPI::SUM,0);
  totaltime = MPI::Wtime() - inittime;
  if( myNode == 0 ){
      cout << "Total Results: " << "number of words palindrome: " << n_pali << endl;
      cout << "Total Results: " << "number of words prime: " << n_prime << endl;
      cout << "Total Time: "<< totaltime << endl;
  }
  MPI::Finalize();
}
